package math;


public class Exp implements ArithmeticExpression {

  private ArithmeticExpression argument;
  
  private Exp(ArithmeticExpression arg) {
    this.argument = arg;
  }

  public static ArithmeticExpression createExp(ArithmeticExpression arg) {
    return new Exp(arg);
  }

  @Override
  public ArithmeticExpression derivate(Variable var) {
    return Mult.createMult(argument.derivate(var), this);
  }

  @Override
  public int countMembers() {
    return 1 + argument.countMembers();
  }

  @Override
  public boolean isOne() {
    return argument.isZero();
  }

  @Override
  public int hashCode() {
    return (argument.hashCode() << 1) ^ 893782718;
  }
  
  @Override
  public boolean isZero() {
    return false;
  }

  @Override
  public int getPriority() {
    return 2;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Exp) {
      Exp exp2 = (Exp) obj;
      return exp2.argument.equals(argument);
    } else
      return false;
  }

  @Override
  public ArithmeticExpression simplify() {
    ArithmeticExpression newArg = argument.simplify();
    if (newArg instanceof Ln){
      Ln ln = (Ln) newArg;
      return ln.getArgument();
    }
    if (newArg.isZero())
      return IntegerConstant.ONE;
//    if (newArg instanceof Product){
//      // exp(a*ln(b)) --> b^a
//      Product prod = (Product) newArg;
//      ArithmeticExpression[] factors = prod.getFactors();
//      Ln ln = null;
//      for (ArithmeticExpression factor: factors){
//        if (factor instanceof Ln){
//          ln = (Ln) factor;
//          break;
//        }
//      }
//      if (ln != null){
//        List<ArithmeticExpression> expFactors = new ArrayList<ArithmeticExpression>(factors.length-1);
//        for (ArithmeticExpression factor: factors)
//          if (factor != ln)
//            expFactors.add(factor);
//        ArithmeticExpression newExp = Product.createProduct(expFactors);
//        return Pow.createPow(ln.getArgument(), newExp).simplify();
//      }
//    }
    
    if (newArg == argument)
      return this;
    else
      return Exp.createExp(newArg);
  }

  @Override
  public String toString() {
    return "exp(" + argument.toString() + ")";
  }


  @Override
  public boolean matches(Expression expr) {
    if (expr instanceof Exp) {
      Exp eExp = (Exp) expr;
      return argument.matches(eExp.getArgument());
    } else
      return false;
  }

  public ArithmeticExpression getArgument() {
    return argument;
  }

  @Override
  public <R> R accept(ExpressionVisitor<R> visitor) {
    return visitor.visitExp(this);
  }
}
